#include "wifi_api.h"
#include "system_ctl.h"
#include "tcpip_adapter.h"
#include "esp_smartconfig.h"
#include "smartconfig_ack.h"

static const char *TAG = "wifi_example";
static const int ESPTOUCH_DONE_BIT = BIT1;
const int CONNECTED_BIT = BIT0;

xTaskHandle samrtxHandle;
/* FreeRTOS event group to signal when we are connected & ready to make a request */
EventGroupHandle_t wifi_event_group;
void smartconfig_task(void *parm);

static void sta_mode_init(void);
static void ap_mode_init(void);
static void close_wifi(void);

Wifi wifi = {
    .ssid = EXAMPLE_WIFI_SSID,
    .passwd = EXAMPLE_WIFI_PASS,
    .wifi_status = SYS_WIFI_STATUS_UNCONNECT,
    .init_sta = sta_mode_init,
    .init_ap = ap_mode_init,
    .close_wifi = close_wifi,
};

static esp_err_t event_handler(void *ctx, system_event_t *event)
{
    /* For accessing reason codes in case of disconnection */
    system_event_info_t *info = &event->event_info;

    switch (event->event_id)
    {
    case SYSTEM_EVENT_STA_START:
        ESP_LOGI(TAG, "[ SYSTEM_EVENT_STA_START ]");
        esp_wifi_connect();
        wifi.wifi_status = SYS_WIFI_STATUS_CONNECTING;
        break;

    case SYSTEM_EVENT_STA_GOT_IP:
        ESP_LOGI(TAG, "[ SYSTEM_EVENT_STA_GOT_IP ]");
        xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
        wifi.wifi_status = SYS_WIFI_STATUS_CONNECTED;
        break;

    case SYSTEM_EVENT_STA_DISCONNECTED:
        ESP_LOGI(TAG, "[ SYSTEM_EVENT_STA_DISCONNECTED ]");
        ESP_LOGE(TAG, "Disconnect reason : %d", info->disconnected.reason);
        if (info->disconnected.reason == WIFI_REASON_BASIC_RATE_NOT_SUPPORT)
        {
            /*Switch to 802.11 bgn mode */
            esp_wifi_set_protocol(ESP_IF_WIFI_STA, WIFI_PROTOCAL_11B | WIFI_PROTOCAL_11G | WIFI_PROTOCAL_11N);
        }
        else if (info->disconnected.reason == WIFI_REASON_NO_AP_FOUND || info->disconnected.reason == WIFI_REASON_MIC_FAILURE || info->disconnected.reason == WIFI_REASON_AUTH_FAIL)
        {
            /* 进入智能配网模式 */
            ESP_LOGI(TAG, "[ smartconfig_example_task ]");
            xTaskCreate(smartconfig_task, "smartconfig_task", 4096, NULL, 3, &samrtxHandle);
        }
        xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
        break;

    case SYSTEM_EVENT_AP_STACONNECTED:
    case SYSTEM_EVENT_AP_STAIPASSIGNED:
        xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
        break;
    default:
        break;
    }

    return ESP_OK;
}

static void sc_callback(smartconfig_status_t status, void *pdata)
{
    switch (status)
    {
    case SC_STATUS_WAIT:
        ESP_LOGI(TAG, "SC_STATUS_WAIT");
        break;
    case SC_STATUS_FIND_CHANNEL:
        ESP_LOGI(TAG, "SC_STATUS_FINDING_CHANNEL");
        break;
    case SC_STATUS_GETTING_SSID_PSWD:
        ESP_LOGI(TAG, "SC_STATUS_GETTING_SSID_PSWD");
        break;
    case SC_STATUS_LINK:
        ESP_LOGI(TAG, "SC_STATUS_LINK");
        wifi_config_t *wifi_config = pdata;
        ESP_LOGI(TAG, "SSID:%s", wifi_config->sta.ssid);
        ESP_LOGI(TAG, "PASSWORD:%s", wifi_config->sta.password);

        strcpy(wifi.ssid, (char *)wifi_config->sta.ssid);
        strcpy(wifi.passwd, (char *)wifi_config->sta.password);
        flash_set_value_str(SYSTEM_VALUE, SYSTEM_WIFI_SSID, wifi.ssid);
        flash_set_value_str(SYSTEM_VALUE, SYSTEM_WIFI_PASS, wifi.passwd);
        flash_commit();

        ESP_ERROR_CHECK(esp_wifi_disconnect());
        ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, wifi_config));
        ESP_ERROR_CHECK(esp_wifi_connect());
        break;
    case SC_STATUS_LINK_OVER:
        ESP_LOGI(TAG, "SC_STATUS_LINK_OVER");
        if (pdata != NULL)
        {
            sc_callback_data_t *sc_callback_data = (sc_callback_data_t *)pdata;
            switch (sc_callback_data->type)
            {
            case SC_ACK_TYPE_ESPTOUCH:
                ESP_LOGI(TAG, "Phone ip: %d.%d.%d.%d", sc_callback_data->ip[0], sc_callback_data->ip[1], sc_callback_data->ip[2], sc_callback_data->ip[3]);
                ESP_LOGI(TAG, "TYPE: ESPTOUCH");
                break;
            case SC_ACK_TYPE_AIRKISS:
                ESP_LOGI(TAG, "TYPE: AIRKISS");
                break;
            default:
                ESP_LOGE(TAG, "TYPE: ERROR");
                break;
            }
        }
        xEventGroupSetBits(wifi_event_group, ESPTOUCH_DONE_BIT);
        break;

    default:
        break;
    }
}

void smartconfig_task(void *parm)
{
    EventBits_t uxBits;

    ESP_ERROR_CHECK(esp_smartconfig_set_type(SC_TYPE_ESPTOUCH_AIRKISS));
    ESP_ERROR_CHECK(esp_smartconfig_start(sc_callback));
    wifi.wifi_status = SYS_WIFI_STATUS_SAMRTCONFIG;

    uxBits = xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, 0x2328); //55s
    if (uxBits & CONNECTED_BIT)
    {
        ESP_LOGI(TAG, "WiFi Connected to ap");
        wifi.wifi_status = SYS_WIFI_STATUS_CONNECTED;
        ESP_LOGI(TAG, "smartconfig over");
        esp_smartconfig_stop();
        vTaskDelete(NULL);
        return;
    }
    if (uxBits & ESPTOUCH_DONE_BIT)
    {
        ESP_LOGI(TAG, "smartconfig over");
    }
    printf("=================== TASK OVER ====================\n");
    esp_smartconfig_stop();
    esp_wifi_deinit();
    esp_wifi_stop();
    esp_wifi_restore();
    wifi.wifi_status = SYS_WIFI_STATUS_CONNECT_ERR;
    vTaskDelete(NULL);
}
//2000 - 120*100ms = 12s
//4000 24
//6000 48
//8000 60
//9000
static void sta_mode_init(void)
{
    wifi_mode_t mode = 0;
    esp_wifi_deinit();
    esp_wifi_stop();

    tcpip_adapter_init();
    wifi_event_group = xEventGroupCreate();
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));

    wifi_config_t wifi_config;
    memset(&wifi_config, 0, sizeof(wifi_config));
    memcpy(wifi_config.sta.ssid, wifi.ssid, strlen(wifi.ssid));
    memcpy(wifi_config.sta.password, wifi.passwd, strlen(wifi.passwd));

    ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
    esp_wifi_get_mode(&mode);
    if (mode != WIFI_MODE_STA)
    {
        ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    }

    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
}

static void ap_mode_init(void)
{
    //esp_wifi_stop();
    esp_wifi_restore();
    //esp_wifi_init();
    xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    wifi_config_t wifi_config = {
        .ap = {
            .ssid = AP_DIGIMON_WIFI_SSID,
            .ssid_len = strlen(AP_DIGIMON_WIFI_SSID),
            .password = AP_DIGIMON_WIFI_PASS,
            .max_connection = AP_DIGIMON_MAX_STA_CONN,
            .authmode = WIFI_AUTH_WPA_WPA2_PSK},
    };

    if (strlen(AP_DIGIMON_WIFI_PASS) == 0)
    {
        wifi_config.ap.authmode = WIFI_AUTH_OPEN;
    }

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));

    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "wifi_init_softap finished.SSID:%s password:%s",
             AP_DIGIMON_WIFI_SSID, AP_DIGIMON_WIFI_PASS);
}

static void close_wifi(void)
{
    wifi.wifi_status = SYS_WIFI_STATUS_UNCONNECT;
    esp_wifi_stop();
}

void wifi_init(void)
{
    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
}